#pragma once
#include "Sem.hpp"
#include "Mutex.hpp"
#include <vector>
using namespace std;

// 用数组模拟实现环形队列
#define cap 5

template <typename T>
class RingQueue
{
public:
    RingQueue(int s = cap)
        : _rq(s), _csem(0), _date_step(0), _psem(s), _blank_step(0) // 总容量是比循环队列少一个的
    {
    }

    void Equeue(T &num)
    {
        // 生产者

        // 申请信号量
        _psem.P();
        // 可以先申请信号量，再加锁。也可以先加锁，再申请信号量。
        // 但是先让线程瓜分完资源再排队，效率会高一些。
        // 其次信号申请操作本身就是原子的，没有必要加锁。

        {
            // 加锁与解锁：维护多生产与多消费下，生产者之间、消费者之前的互斥关系
            LockGuard lockguard(_pmutex);

            // 放入数据
            int size = _rq.size();
            _rq[_date_step++] = num;
            // 维护环形队列性质
            _date_step %= size;
        }

        // 更新消费者的信号量
        _csem.V();
    }

    // 局部变量不能返回引用，会报错：越界访问
    T Pop()
    {
        // 消费者

        // 申请信号量
        _csem.P();

        T num;
        {
            // 加锁与解锁：维护多生产与多消费下，生产者之间、消费者之前的互斥关系
            LockGuard lockguard(_cmutex);

            // 消费资源
            int size = _rq.size();
            num = _rq[_blank_step++];
            // 维护环形队列性质
            _blank_step %= size;
        }

        // 更新生产者的信号量
        _psem.V();
        return num;
    }

private:
    // 用数组模拟实现
    vector<T> _rq;

    // 消费者
    Sem _csem;
    int _date_step; // 不空位置下标
    // 生产者
    Sem _psem;
    int _blank_step; // 空位置下标
    // 两把锁
    Mutex _pmutex;
    Mutex _cmutex;
};